# frozen_string_literal: true

desc 'Generate syscall arguments from Linux:include/linux/syscalls.h'
task :sys_arg do
  content = IO.binread('tasks/syscalls.h') + IO.binread('tasks/syscalls_complement.h')

  def error(name, args, msg)
    puts "[ERROR] #{msg}\n\t#{name}(#{args})"
  end

  def parse_args(args)
    return [] if args == 'void'

    args = args.gsub(/\s+/m, ' ')
    args = args.split(', ').map { |c| c.scan(/ *?(\w+)$/).flatten.first }
    return nil unless args.all?
    return nil if %w[int long].any? { |t| args.include?(t) }

    args
  end

  parse_failed = {}
  proto = content.scan(/^asmlinkage long sys_(\w+)\(([^)]+)\);/m).each_with_object({}) do |(name, args), hash|
    next if name == 'sigsuspend' # there're two implementations, don't know which one should be used
    next error(name, args, 'dup') if hash.key?(name)

    parse_args(args).tap do |res|
      res.nil? ? parse_failed[name] = args : hash[name] = res
    end
  end
  parse_failed.each do |name, args|
    error(name, args, 'parse args faield') unless proto.key?(name)
  end
  IO.binwrite(File.join('lib', 'seccomp-tools', 'consts', 'sys_arg.rb'), <<~RUBY)
    # frozen_string_literal: true

    # Generated by `bundle exec rake sys_arg`

    {
    #{proto.map { |key, value| "  #{key}: %w[#{value.join(' ')}]" }.join(",\n")}
    }
  RUBY
end
